﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "DynamicValueInputPanel.h"

#include <QtWidgets/QPushButton>
#include <QtWidgets/QBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QRadioButton>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QLineEdit>
#include <QtGui/QRegExpValidator>

#include "IUiManager.h"
#include "IDockManager.h"
#include "IDock.h"
#include "GbmpQuickCast.h"

#include "GnufInfo.h"
#include "GnufBoolValueInput.h"
#include "GnufTextValueInput.h"
#include "GnufBoolValueInputGroup.h"
#include "GnufListValueInput.h"

#include "GnufNotificationCenter.h"
#include "GnufNotificationCenterEventArgs.h"

using namespace gcmp;

namespace Sample
{
    constexpr auto DynamicValueInputPanelName = L"DynamicValueInputPanel";

    void clearLayout(QLayout* layout)
    {
        if (layout) {
            while (layout->count() > 0) {
                QLayoutItem* item = layout->takeAt(0);
                if (item) {
                    delete item->widget();
                    delete item;
                }
            }
        }
    }

    class NameValuePairsBuilder
    {
    public:
        explicit NameValuePairsBuilder(QFormLayout* pLayout)
            :m_pLayout(pLayout) { };

        void build(gnuf::ValueInputSet* pValueInputSet);
        static void Refresh();
    private:
        void build(gnuf::BoolValueInput* pInput);
        void build(gnuf::BoolValueInputGroup* pInput);
        void build(gnuf::ListValueInput* pInput);
        void build(gnuf::TextValueInput* pInput);
    private:
        QFormLayout* m_pLayout{};
        //用来同步valueinput状态到Qt控件
        static std::vector<std::pair<QWidget*, gnuf::ValueInputBase*>> m_stubs;
    };
    std::vector<std::pair<QWidget*, gnuf::ValueInputBase*>> NameValuePairsBuilder::m_stubs{};

    void NameValuePairsBuilder::build(gnuf::ValueInputSet* pValueInputSet)
    {
        m_stubs.clear();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pValueInputSet, L"断言`pValueInputSet`失败",L"GDMPLab",L"2024-03-30");
        for (auto& obj : pValueInputSet->rawContents())
        {
            if (gnuf::BoolValueInput* pBInput = quick_cast<gnuf::BoolValueInput>(obj))
            {
                build(pBInput);
            }
            else if (gnuf::TextValueInput* pTInput = quick_cast<gnuf::TextValueInput>(obj))
            {
                build(pTInput);
            }
            else if (gnuf::BoolValueInputGroup* pBGInput = quick_cast<gnuf::BoolValueInputGroup>(obj))
            {
                build(pBGInput);
            }
            else if (gnuf::ListValueInput* pInput = quick_cast<gnuf::ListValueInput>(obj))
            {
                build(pInput);
            }
            else
            {
                //GBMP_ASSERT(false);
                DebugBreak();
            }
        }
    }

    void NameValuePairsBuilder::Refresh()
    {
        for (auto& stub : m_stubs) {
            QWidget* pEditor = stub.first;
            if (!pEditor) continue;
            
            if (gnuf::TextValueInput* pInput = dynamic_cast<gnuf::TextValueInput*>(stub.second))
            {
                pEditor->setEnabled(
                    pInput->textControl()? pInput->textControl()->isEnabled():false
                );

                QLineEdit* pLineEdit = dynamic_cast<QLineEdit*>(pEditor);
                if (pLineEdit)
                {
                    pLineEdit->setText(QString::fromStdWString(pInput->value()));
                }
            }
            else if (gnuf::BoolValueInputGroup* pInput = dynamic_cast<gnuf::BoolValueInputGroup*>(stub.second))
            {
                pEditor->setEnabled(
                    pInput->checkGroupControl() ? pInput->checkGroupControl()->isEnabled() : false
                );
            }
            else if (gnuf::ListValueInput* pInput = dynamic_cast<gnuf::ListValueInput*>(stub.second))
            {
                pEditor->setEnabled(
                    pInput->listControl() ? pInput->listControl()->isEnabled() : false
                );
            }
        }
    }

    void NameValuePairsBuilder::build(gnuf::BoolValueInput* pInput)
    {
        if (!pInput) return;
        auto pControl = pInput->checkControl();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pControl, L"断言`pControl`失败",L"GDMPLab",L"2024-03-30");

        QCheckBox* pEditor = new QCheckBox();
        pEditor->setCheckState(pInput->value() ? Qt::CheckState::Checked : Qt::Unchecked);
        pEditor->setEnabled(pControl->isEnabled());
        QObject::connect(pEditor, &QCheckBox::stateChanged, 
            [=](int iState) {
                pInput->setValue(iState == static_cast<int>(Qt::CheckState::Checked));
                gnuf::NotificationCenter* pNotificationCenter = gnuf::NotificationCenter::get();
                if (pNotificationCenter) {
                    gnuf::ValueInputChangeEventArgs event{ pInput };
                    pNotificationCenter->send(gnuf::ENotificationChannel::eOutput, &event);
                }
                Refresh();
        });
        
        auto pInfo = pControl->info();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInfo, L"断言`pInfo`失败",L"GDMPLab",L"2024-03-30");
        QLabel* pLabel = new QLabel(QString::fromStdWString(pInfo->caption()));
        m_pLayout->addRow(pLabel,pEditor);
        m_stubs.emplace_back(pEditor, pInput);
    }

    void NameValuePairsBuilder::build(gnuf::BoolValueInputGroup* pInput)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInput, L"断言`pInput`失败",L"GDMPLab",L"2024-03-30");
        auto pControl = pInput->checkGroupControl();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pControl, L"断言`pControl`失败",L"GDMPLab",L"2024-03-30");

        QGroupBox* pEditor = new QGroupBox();
        pEditor->setEnabled(pControl->isEnabled());

        QVBoxLayout* pLayout = new QVBoxLayout();
        pEditor->setLayout(pLayout);
        std::vector<const gnuf::Info*> rawList;
        pControl->GetList(rawList);
        const std::vector<bool>& checkStatus = pControl->GetCheckStatus();
        for (std::size_t i = 0; i < rawList.size(); i++)
        {
            auto& pInfo = rawList.at(i);
            QRadioButton* pButton = new QRadioButton(QString::fromStdWString(pInfo->caption()));
            pButton->setChecked(checkStatus.at(i));
            QObject::connect(pButton, &QRadioButton::clicked,
                [=](bool bValue) {
                    pInput->setValue(std::make_pair(static_cast<int>(i), bValue));
                    gnuf::NotificationCenter* pNotificationCenter = gnuf::NotificationCenter::get();
                    if (pNotificationCenter) {
                        gnuf::ValueInputChangeEventArgs event{ pInput };
                        pNotificationCenter->send(gnuf::ENotificationChannel::eOutput,&event);
                    }
                    Refresh();
                });
            pLayout->addWidget(pButton);
        }
        auto pInfo = pControl->info();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInfo, L"断言`pInfo`失败",L"GDMPLab",L"2024-03-30");
        QLabel* pLabel = new QLabel(QString::fromStdWString(pInfo->caption()));
        m_pLayout->addRow(pLabel, pEditor);
        m_stubs.emplace_back(pEditor, pInput);
    }

    void NameValuePairsBuilder::build(gnuf::ListValueInput* pInput)
    {
        if (!pInput) return;

        auto pControl = pInput->listControl();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pControl, L"断言`pControl`失败",L"GDMPLab",L"2024-03-30");

        QComboBox* pEditor = new QComboBox();
        for (auto& item : pControl->GetList()) 
        {
            pEditor->addItem(QString::fromStdWString(item->caption()));
        }
        pEditor->setCurrentIndex(pInput->value());
        pEditor->setEnabled(pControl->isEnabled());
        
        QObject::connect(pEditor, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            [=](int index) 
            {
            pInput->setValue(index);
            gnuf::NotificationCenter* pNotificationCenter = gnuf::NotificationCenter::get();
            if (pNotificationCenter) {
                gnuf::ValueInputChangeEventArgs event{ pInput };
                pNotificationCenter->send(gnuf::ENotificationChannel::eOutput, &event);
            }
            Refresh();
            });

        auto pInfo = pControl->info();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInfo, L"断言`pInfo`失败",L"GDMPLab",L"2024-03-30");
        QLabel* pLabel = new QLabel(QString::fromStdWString(pInfo->caption()));
        m_pLayout->addRow(pLabel, pEditor);
        m_stubs.emplace_back(pEditor, pInput);
    }

    void NameValuePairsBuilder::build(gnuf::TextValueInput* pInput)
    {
        if (!pInput) return;

        auto pControl = pInput->textControl();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pControl, L"断言`pControl`失败",L"GDMPLab",L"2024-03-30");

        auto pEditor = new QLineEdit;
        pEditor->setText(QString::fromStdWString(pInput->value()));
        if (!pInput->validator().empty()) {
            QRegExp regx(QString::fromStdWString(pInput->validator()));
            pEditor->setValidator(new QRegExpValidator(regx,pEditor));
            pEditor->setCompleter(nullptr);
        }
        
        QObject::connect(pEditor, &QLineEdit::editingFinished, [=]() {
            pInput->setValue(pEditor->text().toStdWString());
            gnuf::NotificationCenter* pNotificationCenter = gnuf::NotificationCenter::get();
            if (pNotificationCenter) {
                gnuf::ValueInputChangeEventArgs event{ pInput };
                pNotificationCenter->send(gnuf::ENotificationChannel::eOutput, &event);
            }
            Refresh();
            });

        pEditor->setEnabled(pControl->isEnabled());

        auto pInfo = pControl->info();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pInfo, L"断言`pInfo`失败",L"GDMPLab",L"2024-03-30");
        QLabel* pLabel = new QLabel(QString::fromStdWString(pInfo->caption()));
        m_pLayout->addRow(pLabel, pEditor);
        m_stubs.emplace_back(pEditor, pInput);
    }

    DynamicValueInputPanel::DynamicValueInputPanel(QWidget* parent, Qt::WindowFlags f)
        :QWidget(parent,f)
    {
        m_pLayout = new QFormLayout();
        setLayout(m_pLayout);
    }

    DynamicValueInputPanel* DynamicValueInputPanel::Get(bool bAutoCreate)
    {
        IUiManager* pUiManager = IUiManager::Get();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiManager, L"断言`pUiManager`失败",L"GDMPLab",L"2024-03-30");
        IDockManager* pDockManager = pUiManager->GetDockManager();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDockManager, L"断言`pDockManager`失败",L"GDMPLab",L"2024-03-30");

        DynamicValueInputPanel* pPanel = nullptr;
        {
            IDock* pDock = pDockManager->GetDock(DynamicValueInputPanelName);
            if (pDock) {
                pPanel = dynamic_cast<DynamicValueInputPanel*>(
                    pDock->GetWidget()
                    );
            }
        }
        if (!bAutoCreate) {
            return pPanel;
        }

        if (!pPanel) {

            OwnerPtr<IDock> opDock = pDockManager->CreateDock(
                DynamicValueInputPanelName,
                L"预设参数"
            );
            OwnerPtr<DynamicValueInputPanel> opPanel = NEW_AS_OWNER_PTR(DynamicValueInputPanel);
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opPanel, L"断言`opPanel`失败",L"GDMPLab",L"2024-03-30");
            opPanel->m_pDock = opDock.get();
            pPanel = opPanel.get();
            opDock->SetWidget(std::move(opPanel));
            opDock->SetFloating(true);
            pDockManager->AddDock(std::move(opDock),
                gcmp::DockArea::Right, gcmp::DockOrientation::Vertical);
        }
        return pPanel;
    }

    void DynamicValueInputPanel::Show()
    {
        if (m_pDock) {
            m_pDock->Show();
            m_pDock->Raise();
        }
        else
        {
            this->show();
        }
    }

    void DynamicValueInputPanel::Hide()
    {
        if (m_pDock) {
            m_pDock->Hide();
        }
        else
        {
            this->hide();
        }
    }

    void DynamicValueInputPanel::SetCaption(const std::wstring& caption)
    {
        if (m_pDock) {
            m_pDock->SetTitle(caption);
        }
        else {
            this->setWindowTitle(QString::fromStdWString(caption));
        }
    }

    void DynamicValueInputPanel::Update(const std::wstring& caption, gnuf::ValueInputSet* pValueInputSet)
    {
        SetCaption(caption);
        clearLayout(m_pLayout);
        if (pValueInputSet && !pValueInputSet->contents().empty()) {
            NameValuePairsBuilder{ m_pLayout }.build(pValueInputSet);
            Show();
        }
        else
        {
            Hide();
        }
    }

    void DynamicValueInputPanel::SetEnabled(bool bEnable)
    {
        setEnabled(bEnable);
    }
}
